package com.gitee.elf.api.nms.v1_12_R1

import com.gitee.elf.api.annotation.EntityId
import com.gitee.elf.api.nms.NMSEntity
import com.gitee.elf.api.nms.nmsWorld
import com.gitee.elf.core.entity.EntityInstance
import com.gitee.elf.core.entity.EntityManager.bukkitMappingInstance
import net.minecraft.server.v1_12_R1.*
import org.bukkit.Bukkit
import org.bukkit.Location
import org.bukkit.entity.EntityType
import taboolib.common5.cfloat

@EntityId("wolf", EntityType.WOLF)
class NMSWolf(val instance: EntityInstance,location: Location) : EntityWolf(location.nmsWorld()), NMSEntity {


    companion object {

        val isJump = EntityLiving::class.java.getDeclaredField("bd").let {
            it.isAccessible = true
            it
        }

    }


    val moveSpeed : Float
        get() = instance.bukkitMappingInstance!!.moveSpeed.get().value

    val jumpHeight : Double
        get() = instance.bukkitMappingInstance!!.jumpHeight.get().value

    val ascendSpeed : Double
        get() = instance.bukkitMappingInstance!!.ascendSpeed.get().value

    protected var isJumpOrFly = false // 默认设置不跳跃或不飞行

    // 防止摔伤
    override fun e(f: Float, f1: Float) {
        if (!this.isJumpOrFly) {
            super.e(f, f1)
        }
    }

    override fun a(motionSideways: Float, motionForward: Float, f: Float) {
        var motionSideways = motionSideways
        var motionForward = motionForward
        if (passengers != null && !passengers.isEmpty()) { // 判断有无乘客
            val passenger = passengers[0] as EntityLiving // 获取坐骑列表第一个乘客(驾驶员2333)
            val passengerUUID = passenger.uniqueID // 玩家的UUID
            val player = Bukkit.getPlayer(passengerUUID) ?: return
            val canFly: Boolean = player.hasPermission("xxx.fly") // 是否有飞行权限
            if (onGround) { // 是否落到地面
                isJumpOrFly = false // 设置为不跳跃不飞行
            }

            // 设置实体与乘客一样的俯仰角，航向角
            lastYaw = passenger.yaw.also { yaw = it }
            pitch = passenger.pitch * 0.5f
            setYawPitch(yaw, pitch)
            aP = yaw.also { aN = it }
            motionSideways = passenger.be * 0.5f // 设置左右运动
            motionForward = passenger.bg // 设置前后运动

            // 向后放慢
            if (motionForward <= 0.0f) {
                motionForward *= 0.25f
            }
            motionSideways *= 0.8f // 左右慢，但不如向后慢
            val speed = moveSpeed // 实体行走速度
            val jumpHeight = jumpHeight // 实体跳跃高度
            val ascendSpeed = ascendSpeed // 实体飞行速度

            // 触发坐骑方法
            ride(motionSideways, motionForward, f, speed)
            if (this.isVehicle) {
                var doJump = false
                try {
                    doJump = isJump.getBoolean(passenger) // 触发跳跃或飞行
                } catch (ignored: IllegalAccessException) {

                }
                if (doJump) {
                    if (canFly) { // 是否可以飞行
                        if (motY < ascendSpeed) {
                            motY = ascendSpeed // 设置向上的飞行移动
                            isJumpOrFly = true
                        }
                    } else {
                        if (onGround) { // 如果在地面则可跳跃
                            motY = jumpHeight // 设置向上的跳跃
                            isJumpOrFly = true
                        }
                    }
                }
            }
        }
        super.a(motionSideways, motionForward, f)
    }

    // 实现坐骑(MyPet开源项目有很好的算法：https://github.com/xXKeyleXx/MyPet)
    private fun ride(motionSideways: Float, f: Float, motionForward: Float, speedModifier: Float) {
        var locY: Double
        var f2: Float
        val swimSpeed: Float
        val speed: Float
        if (this.isInWater) { // 是否在水中
            locY = this.locY // Y轴的移动
            speed = 0.8f // 赋予实体速度
            swimSpeed = 0.02f // 赋予实体水中速度

            // 开始设置实体水中运动
            this.b(motionSideways, motionForward, f, swimSpeed)
            move(EnumMoveType.SELF, motX, motY, motZ)
            motX *= speed.toDouble()
            motY *= 0.8
            motZ *= speed.toDouble()
            motY -= 0.02
            if (positionChanged && this.c(motX, motY + 0.6000000238418579 - this.locY + locY, motZ)) { // 实现真实游泳状态
                motY = 0.3
            }
        } else if (au()) { // 在熔岩中
            // 设置实体在熔岩的运动，让它下沉，无法上来
            locY = this.locY
            this.b(motionSideways, motionForward, f, 0.02f)
            move(EnumMoveType.SELF, motX, motY, motZ)
            motX *= 0.5
            motY *= 0.5
            motZ *= 0.5
            motY -= 0.02
            if (positionChanged && this.c(motX, motY + 0.6 - this.locY + locY, motZ)) {
                motY = 0.3
            }
        } else {
            // 除了以上环境
            // 实现操控实体地面/空中移动算法
            var friction = 0.91f
            if (onGround) { // 在地面
                friction = world.getType(
                    BlockPosition(
                        MathHelper.floor(locX),
                        MathHelper.floor(boundingBox.b) - 1,
                        MathHelper.floor(locZ)
                    )
                ).block.frictionFactor * 0.91f
            }
            speed = speedModifier * (0.2f / (friction * friction * friction)) // 此时实体运动速度
            this.b(motionSideways, motionForward, f, speed)
            friction = 0.9f
            if (onGround) {
                friction = world.getType(
                    BlockPosition(
                        MathHelper.floor(locX),
                        MathHelper.floor(boundingBox.b) - 1,
                        MathHelper.floor(locZ)
                    )
                ).block.frictionFactor * 0.91f
            }
            if (m_()) {
                swimSpeed = 0.15f
                motX = MathHelper.a(motX.toFloat(), -swimSpeed, swimSpeed).toDouble()
                motZ = MathHelper.a(motZ.toFloat(), -swimSpeed, swimSpeed).toDouble()
                fallDistance = 0.0f
                if (motY < -0.15) {
                    motY = -0.15
                }
            }
            move(EnumMoveType.SELF, motX, motY, motZ)
            if (positionChanged && m_()) {
                motY = 0.2
            }
            motY -= 0.08
            motY *= 1.0
            motX *= friction.toDouble()
            motZ *= friction.toDouble()
        }
        aF = aG
        locY = locX - lastX
        val d1 = locZ - lastZ
        f2 = MathHelper.sqrt(locY * locY + d1 * d1) * 4.0f
        if (f2 > 1.0f) {
            f2 = 1.0f
        }
        aG += (f2 - aG) * 0.4f
        aH += aG
    }

}